package com.mlethe.library.dialog;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager;

import java.lang.reflect.Field;

/**
 * 自定义DialogFragment
 * 解决DialogFragment的内存泄漏
 *
 * @author Mlethe
 * @date 2020/8/4
 */
public abstract class DialogFragment extends androidx.fragment.app.DialogFragment {

    private static final String SAVED_CANCELABLE = "android:cancelable";
    private static final String SAVED_CANCELABLE_OUTSIDE = "android:cancelableOnTouchOutside";
    private static final String SAVED_DIALOG_WIDTH = "android:dialogWidth";
    private static final String SAVED_DIALOG_HEIGHT = "android:dialogHeight";
    private static final String SAVED_DIALOG_GRAVITY = "android:dialogGravity";
    private static final String SAVED_DIALOG_IS_TRANSPARENT = "android:isTransparent";
    private static final String SAVED_DIALOG_IS_EXTEND = "android:isExtend";
    private static final String SAVED_DIALOG_LOCATION_X = "android:dialogLocationX";
    private static final String SAVED_DIALOG_LOCATION_Y = "android:dialogLocationY";
    private static final String SAVED_DIALOG_AMOUNT = "android:dialogAmount";
    private static final String SAVED_DIALOG_IS_NO_TITLE = "android:isNoTitle";
    private static final String SAVED_DIALOG_ANIMATIONS = "android:dialogAnimations";

    /**
     * 点击空白或返回按钮是否能够取消，默认可以取消
     */
    private boolean mCancelable = true;
    /**
     * 点击空白是否能够取消，默认点击阴影不可以取消
     */
    private boolean mCancelableOnTouchOutside = false;
    /**
     * 弹框宽度、高度
     */
    private int mWidth = -10, mHeight = -10;
    /**
     * 位置
     */
    protected int mGravity = 0;
    /**
     * 背景是否透明
     */
    private boolean mIsTransparent;
    /**
     * 高度不是 MATCH_PARENT ，异形屏是否延伸
     */
    private boolean mIsExtend;
    /**
     * 弹框x、y坐标
     */
    protected float x = 0, y = 0;
    /**
     * 遮罩透明度
     */
    protected float mAmount = -1;
    /**
     * 标题是否显示
     */
    protected boolean mIsNoTitle = false;
    /**
     * style动画
     */
    protected int mAnimation = 0;
    /**
     * 弹框关闭监听
     */
    private DialogInterface.OnDismissListener mOnDismissListener;
    /**
     * dialog Key监听
     */
    private DialogInterface.OnKeyListener mOnKeyListener;

    @NonNull
    @Override
    public LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState) {
        LayoutInflater layoutInflater = super.onGetLayoutInflater(savedInstanceState);
        Dialog dialog = getDialog();
        if (dialog == null) {
            return layoutInflater;
        }
        dialog.setCancelable(getCancelEnable());
        DialogInterface.OnKeyListener onKeyListener = getOnKeyListener();
        if (onKeyListener != null) {
            dialog.setOnKeyListener(onKeyListener);
        }
        dialog.setOnCancelListener(null);
        dialog.setOnDismissListener(dialog1 -> {
            dialog.setOnDismissListener(null);
            onDismiss(dialog1);
        });
        Window window = dialog.getWindow();
        if (window == null) {
            return layoutInflater;
        }
        window.getDecorView().setOnTouchListener((v, event) -> {
            if (event == null) return false;
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                if (getCancelEnable()) {
                    if (getCancelableOnTouchOutside() && !onTouch(event)) {
                        dismissAllowingStateLoss();
                        return true;
                    }
                }
            }
            return false;
        });
        int width = getWidth();
        int height = getHeight();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // android 11.0以上，包含11.0
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
            window.setNavigationBarColor(Color.WHITE);
        }
        if (getBackgroundTransparent(width, height)) {
            //设置背景为透明
            Context context = getContext();
            if (context != null) {
                window.setBackgroundDrawable(ContextCompat.getDrawable(context, android.R.color.transparent));
            }
        }
        int gravity = getGravity();
        if (gravity != 0) {
            window.setGravity(gravity);
        }
        WindowManager.LayoutParams params = window.getAttributes();
        if (isExtend(height)) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                // 异形屏延伸显示区域
                params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
            }
        }
        if (height != -10) {
            params.height = height;
        }
        if (width != -10) {
            params.width = width;
        }
        float x = getX();
        if (x != 0) {
            params.x = (int) x;
        }
        float y = getY();
        if (y != 0) {
            params.y = (int) y;
        }
        window.setAttributes(params);
        // 设置动画
        int animation = getAnimation();
        if (animation != 0) {
            window.setWindowAnimations(animation);
        }
        // 设置遮罩透明度
        float amount = getDimAmount();
        if (amount != -1) {
            window.setDimAmount(amount);
        }
        return layoutInflater;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        return new CustomDialog(requireContext(), getTheme());
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        if (!mCancelable) {
            outState.putBoolean(SAVED_CANCELABLE, false);
        }
        if (mCancelableOnTouchOutside) {
            outState.putBoolean(SAVED_CANCELABLE_OUTSIDE, true);
        }
        if (mWidth != -10) {
            outState.putInt(SAVED_DIALOG_WIDTH, mWidth);
        }
        if (mHeight != -10) {
            outState.putInt(SAVED_DIALOG_HEIGHT, mHeight);
        }
        if (mGravity != 0) {
            outState.putInt(SAVED_DIALOG_GRAVITY, mGravity);
        }
        if (mIsTransparent) {
            outState.putBoolean(SAVED_DIALOG_IS_TRANSPARENT, true);
        }
        if (mIsExtend) {
            outState.putBoolean(SAVED_DIALOG_IS_EXTEND, true);
        }
        if (x != 0) {
            outState.putFloat(SAVED_DIALOG_LOCATION_X, x);
        }
        if (y != 0) {
            outState.putFloat(SAVED_DIALOG_LOCATION_Y, y);
        }
        if (mAmount != -1) {
            outState.putFloat(SAVED_DIALOG_AMOUNT, mAmount);
        }
        if (mIsNoTitle) {
            outState.putBoolean(SAVED_DIALOG_IS_NO_TITLE, true);
        }
        if (mAnimation != 0) {
            outState.putInt(SAVED_DIALOG_ANIMATIONS, mAnimation);
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
            mCancelableOnTouchOutside = savedInstanceState.getBoolean(SAVED_CANCELABLE_OUTSIDE, false);
            mWidth = savedInstanceState.getInt(SAVED_DIALOG_WIDTH, -10);
            mHeight = savedInstanceState.getInt(SAVED_DIALOG_HEIGHT, -10);
            mGravity = savedInstanceState.getInt(SAVED_DIALOG_GRAVITY, 0);
            mIsTransparent = savedInstanceState.getBoolean(SAVED_DIALOG_IS_TRANSPARENT, false);
            mIsExtend = savedInstanceState.getBoolean(SAVED_DIALOG_IS_EXTEND, false);
            x = savedInstanceState.getFloat(SAVED_DIALOG_LOCATION_X, 0);
            y = savedInstanceState.getFloat(SAVED_DIALOG_LOCATION_Y, 0);
            mAmount = savedInstanceState.getFloat(SAVED_DIALOG_AMOUNT, -1);
            mIsNoTitle = savedInstanceState.getBoolean(SAVED_DIALOG_IS_NO_TITLE, false);
            mAnimation = savedInstanceState.getInt(SAVED_DIALOG_ANIMATIONS, 0);
        }
    }

    @Override
    public void onDismiss(@NonNull DialogInterface dialog) {
        super.onDismiss(dialog);
        if (mOnDismissListener != null) {
            mOnDismissListener.onDismiss(dialog);
            mOnDismissListener = null;
        }
    }

    /**
     * 是否触摸到view
     *
     * @param event
     * @return
     */
    public boolean onTouch(MotionEvent event) {
        return isTouchView(getView(), event);
    }

    /**
     * 是否触摸到 view
     *
     * @param view
     * @param event
     * @return
     */
    protected final boolean isTouchView(View view, MotionEvent event) {
        if (view != null) {
            int[] location = new int[2];
            view.getLocationOnScreen(location);
            float x = event.getRawX();
            float y = event.getRawY();
            int left = location[0];
            int right = left + view.getMeasuredWidth();
            int top = location[1];
            int bottom = top + view.getMeasuredHeight();
            return x >= left && x <= right && y >= top && y <= bottom;
        }
        return false;
    }

    /**
     * 显示弹窗
     *
     * @param manager
     */
    public void show(FragmentManager manager) {
        show(manager, getClass().getSimpleName());
    }

    /**
     * 显示弹窗
     *
     * @param manager
     * @param tag
     */
    public void showAllowingStateLoss(@NonNull FragmentManager manager, @Nullable String tag) {
        try {
            Class<?> clazz = Class.forName("androidx.fragment.app.DialogFragment");
            Field dismissedField = clazz.getDeclaredField("mDismissed");
            dismissedField.setAccessible(true);
            dismissedField.set(this, false);
            Field shownByMeField = clazz.getDeclaredField("mShownByMe");
            shownByMeField.setAccessible(true);
            shownByMeField.set(this, true);
            manager.beginTransaction()
                    .add(this, tag)
                    .commitNowAllowingStateLoss();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 显示弹窗
     *
     * @param manager
     */
    public void showAllowingStateLoss(FragmentManager manager) {
        showAllowingStateLoss(manager, getClass().getSimpleName());
    }

    /**
     * 关闭弹窗
     */
    @Override
    public void dismiss() {
        dismissAllowingStateLoss();
    }

    /**
     * 获取是否有标题
     *
     * @return true：没有标题，false：有标题
     */
    protected boolean getNoTitle() {
        return mIsNoTitle;
    }

    /**
     * 设置是否有标题
     *
     * @param isNoTitle true：没有标题，false：有标题
     * @return
     */
    public DialogFragment setNoTitle(boolean isNoTitle) {
        this.mIsNoTitle = isNoTitle;
        return this;
    }

    /**
     * 获取x坐标
     */
    protected float getX() {
        return x;
    }

    /**
     * 获取y坐标
     */
    protected float getY() {
        return y;
    }

    /**
     * 设置x,y坐标
     *
     * @param x
     * @param y
     * @return
     */
    public DialogFragment setXY(float x, float y) {
        this.x = x;
        this.y = y;
        return this;
    }

    /**
     * 获取宽度
     *
     * @return
     */
    protected int getWidth() {
        return mWidth;
    }

    /**
     * 获取高度
     *
     * @return
     */
    protected int getHeight() {
        return mHeight;
    }

    /**
     * 宽度充满
     *
     * @return
     */
    public DialogFragment fullWidth() {
        mWidth = ViewGroup.LayoutParams.MATCH_PARENT;
        return this;
    }

    /**
     * 高度充满
     *
     * @return
     */
    public DialogFragment fullHeight() {
        mHeight = ViewGroup.LayoutParams.MATCH_PARENT;
        return this;
    }

    /**
     * 充满整个屏幕
     *
     * @return
     */
    public DialogFragment full() {
        mWidth = ViewGroup.LayoutParams.MATCH_PARENT;
        mHeight = ViewGroup.LayoutParams.MATCH_PARENT;
        return this;
    }

    /**
     * 获取位置
     *
     * @return
     */
    protected int getGravity() {
        return mGravity;
    }

    /**
     * 设置位置
     *
     * @param gravity
     * @return
     */
    public DialogFragment setGravity(int gravity) {
        mGravity = gravity;
        return this;
    }

    /**
     * 设置dialog的宽度
     *
     * @param width px
     * @return
     */
    public DialogFragment setWidth(int width) {
        mWidth = width;
        return this;
    }

    /**
     * 设置dialog的高度
     *
     * @param height px
     * @return
     */
    public DialogFragment setHeight(int height) {
        mHeight = height;
        return this;
    }

    /**
     * 高度不是 MATCH_PARENT ，异形屏是否延伸，默认不延伸
     *
     * @return
     */
    protected boolean isExtend(int height) {
        if (height == ViewGroup.LayoutParams.MATCH_PARENT) {
            return true;
        }
        return mIsExtend;
    }

    /**
     * 获取背景是否透明
     *
     * @return Boolean
     */
    protected boolean getBackgroundTransparent(int width, int height) {
        if (width == ViewGroup.LayoutParams.MATCH_PARENT || height == ViewGroup.LayoutParams.MATCH_PARENT) {
            return true;
        }
        return mIsTransparent;
    }

    /**
     * 设置背景透明
     *
     * @param isTransparent Boolean
     * @return DialogFragment
     */
    public DialogFragment setBackgroundTransparent(boolean isTransparent) {
        this.mIsTransparent = isTransparent;
        return this;
    }

    /**
     * 点击空白或返回按钮是否能够取消，默认(true)可以取消
     *
     * @return true 可以取消，false 不可以取消
     */
    protected boolean getCancelEnable() {
        return mCancelable;
    }

    /**
     * 点击空白或返回按钮是否能够取消，默认(true)可以取消
     *
     * @param cancelable
     */
    @Override
    public void setCancelable(boolean cancelable) {
        mCancelable = cancelable;
        super.setCancelable(cancelable);
    }

    /**
     * 点击空白或返回按钮是否能够取消，默认(true)可以取消
     *
     * @param cancelable
     */
    public DialogFragment setCancelEnable(boolean cancelable) {
        setCancelable(cancelable);
        return this;
    }

    /**
     * 点击空白是否能够取消，默认(false)点击阴影不可以取消
     *
     * @return true 可以取消，false 不可以取消
     */
    protected boolean getCancelableOnTouchOutside() {
        return mCancelableOnTouchOutside;
    }

    /**
     * 点击空白是否能够取消，默认(false)点击阴影不可以取消
     * Sets whether the dialog is cancelable or not.  Default is true.
     *
     * @return This Builder object to allow for chaining of calls to set methods
     */
    public DialogFragment setCancelableOnTouchOutside(boolean cancelableOnTouchOutside) {
        mCancelableOnTouchOutside = cancelableOnTouchOutside;
        return this;
    }

    /**
     * 获取遮罩透明度
     *
     * @return
     */
    protected float getDimAmount() {
        return mAmount;
    }

    /**
     * 设置遮罩透明度
     *
     * @param amount
     * @return
     */
    public DialogFragment setDimAmount(float amount) {
        this.mAmount = amount;
        return this;
    }

    /**
     * 获取动画
     *
     * @return
     */
    protected int getAnimation() {
        return mAnimation;
    }

    /**
     * 从底部弹出
     *
     * @param isAnimation 是否有动画
     * @return
     */
    public DialogFragment formBottom(boolean isAnimation) {
        if (isAnimation) {
            mAnimation = R.style.dialog_from_bottom_anim;
        }
        mGravity = Gravity.BOTTOM;
        return this;
    }

    /**
     * 添加默认动画
     *
     * @return
     */
    public DialogFragment addDefaultAnimation() {
        mAnimation = R.style.dialog_default_anim;
        return this;
    }

    /**
     * 设置动画
     *
     * @param styleAnimation
     * @return
     */
    public DialogFragment setAnimation(int styleAnimation) {
        mAnimation = styleAnimation;
        return this;
    }

    /**
     * dialog Dismiss监听
     * Sets the callback that will be called when the dialog is dismissed for any reason.
     *
     * @return This Builder object to allow for chaining of calls to set methods
     */
    public DialogFragment setOnDismissListener(DialogInterface.OnDismissListener listener) {
        mOnDismissListener = listener;
        return this;
    }

    /**
     * 获取dialog Key监听
     *
     * @return
     */
    protected DialogInterface.OnKeyListener getOnKeyListener() {
        return mOnKeyListener;
    }

    /**
     * dialog Key监听
     * Sets the callback that will be called if a key is dispatched to the dialog.
     *
     * @return This Builder object to allow for chaining of calls to set methods
     */
    public DialogFragment setOnKeyListener(DialogInterface.OnKeyListener listener) {
        this.mOnKeyListener = listener;
        return this;
    }

    /**
     * 解决屏幕旋转后 状态无法保持
     * 在activity或fragment onCreate()方法中调用
     * 如果返回不为null，重新设置监听或数据
     *
     * @param manager
     * @param tag
     * @param <T>
     * @return
     */
    public static <T extends androidx.fragment.app.DialogFragment> T getDialogFragment(FragmentManager manager, String tag) {
        if (manager != null) {
            return (T) manager.findFragmentByTag(tag);
        }
        return null;
    }
}
